
/**
 * AppleIIGo
 * Bit Renderer class
 * (C) 2008 by Marc S. Ressl (mressl@umich.edu)
 * Released under the GPL
 */

#include "BitRenderer.h"

BitRenderer::BitRenderer(Motherboard * motherboard, UINT32 videoType,
						 UINT32 characterSetType, char * characterSetRomImage) :
VideoRenderer(motherboard, videoType, characterSetType, characterSetRomImage) {
	initDoublePixels();
	initLoresPixels();
	
	horizIndex = 0;
}

void BitRenderer::initDoublePixels() {
	for (UINT32 index = 0; index < BITRENDERER_DOUBLEPIXELS_END; index++) {
		UINT32 singlePixelData = index & 0x7f;
		bool isDelay = index & BITRENDERER_DOUBLEPIXELS_DELAY;
		bool isLastPixel = index & BITRENDERER_DOUBLEPIXELS_LASTBIT;
		
		UINT32 doublePixelData = 0;
		
		for (UINT32 bitIndex = 0; bitIndex < 7; bitIndex++) {
			bool isPixel = (singlePixelData & 0x1);
			singlePixelData >>= 1;
			
			if (!isDelay)
				isLastPixel = isPixel;
			
			doublePixelData >>= 1;
			setBit(doublePixelData, 0x2000, isLastPixel);
			doublePixelData >>= 1;
			setBit(doublePixelData, 0x2000, isPixel);
			
			isLastPixel = isPixel;
		}
		
		doublePixels[index] = doublePixelData;
	}
}

void BitRenderer::initLoresPixels() {
	for (UINT32 index = 0; index < BITRENDERER_LORESPIXELS_END; index++) {
		UINT32 value = index & 0xf;
		if (index & BITRENDERER_LORESPIXELS_ODD)
			loresPixels[index] = (value << 10) | (value << 6) |
			(value << 2) | (value >> 2);
		else
			loresPixels[index] = ((value & 0x3) << 12) | (value << 8) |
			(value << 4) | value;
	}
}

void BitRenderer::renderScanline(UINT32 scanlineIndex, UINT32 horizEnd) {
	UINT32 address = addressMap[scanlineIndex];
	UINT32 textScanline;
	UINT32 loresShift;
	
	switch (renderMap[scanlineIndex]) {
		case RENDERMODE_BORDER:
			for (UINT32 index = horizIndex; index < horizEnd; index++)
				pixels[index] = 0;
			return;
		case RENDERMODE_TEXT:
			textScanline = (scanlineIndex - SCANLINE_APPLEII) & 0x7;
			for (UINT32 index = horizIndex; index < horizEnd; index++) {
				UINT32 videoData = shadowMemory[address + index];
				// TO-DO: Move textScanline indexing out of loop
				UINT32 pixelData = 
				characterSetMap[videoData | characterSetMapFlags][textScanline];
				pixels[index] = doublePixels[pixelData];
			}
			break;
		case RENDERMODE_DTEXT:
			textScanline = (scanlineIndex - SCANLINE_APPLEII) & 0x7;
			for (UINT32 index = horizIndex; index < horizEnd; index++) {
				UINT32 videoData = shadowMemory[address + index + BANK_BYTENUM];
				// TO-DO: Move textScanline indexing out of loop
				UINT32 pixelData = 
				characterSetMap[videoData | characterSetMapFlags][textScanline];
				pixels[index] = pixelData;
				videoData = shadowMemory[address + index];
				// TO-DO: Move textScanline indexing out of loop
				pixelData |= 
					characterSetMap[videoData | characterSetMapFlags]
					[textScanline] << 7;
				pixels[index] = pixelData;
			}
			break;
		case RENDERMODE_LORES:
			loresShift = (scanlineIndex - SCANLINE_APPLEII) & 0x4;
			for (UINT32 index = horizIndex; index < horizEnd; index++) {
				UINT32 videoData = shadowMemory[address + index];
				UINT32 loresColor = (videoData >> loresShift) & 0xf;
				pixels[index] = loresPixels[loresColor | ((index & 0x1) << 4)];
			}
			break;
		case RENDERMODE_DLORES:
			loresShift = (scanlineIndex - SCANLINE_APPLEII) & 0x4;
			for (UINT32 index = horizIndex; index < horizEnd; index++) {
				UINT32 videoData = shadowMemory[address + index + BANK_BYTENUM];
				UINT32 loresColor = (videoData >> loresShift) & 0xf;
				UINT32 pixelData = loresPixels[loresColor |
											   ((index & 0x1) << 4)] & 0x7f;
				videoData = shadowMemory[address + index];
				loresColor = (videoData >> loresShift) & 0xf;
				pixelData |= (loresPixels[loresColor |
										  ((index & 0x1) << 4)] & 0x7f) << 7;
				pixels[index] = pixelData;
			}
			break;
		case RENDERMODE_7MLORES:
			loresShift = (scanlineIndex - SCANLINE_APPLEII) & 0x4;
			for (UINT32 index = horizIndex; index < horizEnd; index++) {
				UINT32 videoData = shadowMemory[address + index];
				UINT32 loresColor = (videoData >> loresShift) & 0xf;
				pixels[index] = doublePixels[loresPixels[loresColor |
														 ((index & 0x1) << 4)]
											 & 0x7f];
			}
			break;
		case RENDERMODE_HIRES:
			UINT32 lastVideoData;
			if (!horizIndex && isAppleIIVideo)
				lastVideoData = shadowMemory[(address & ~0x7f) |
											 ((address - 1) & 0x7f)];
			else
				lastVideoData = 0;
			for (UINT32 index = horizIndex; index < horizEnd; index++) {
				lastVideoData = shadowMemory[address + index] |
				((lastVideoData & 0x40) << 2);
				pixels[index] = doublePixels[lastVideoData];
			}
			break;
		case RENDERMODE_DHIRES:
		case RENDERMODE_DHIRESMONOCHROME:
			for (UINT32 index = horizIndex; index < horizEnd; index++)
				pixels[index] = (shadowMemory[address + index +
											  BANK_BYTENUM] & 0x7f) |
					((shadowMemory[address + index] & 0x7f) << 7);
			break;
		case RENDERMODE_7HIRES:
			for (UINT32 index = horizIndex; index < horizEnd; index++)
				pixels[index] = doublePixels[shadowMemory[address +
														  index] & 0x7f];
			break;
		case RENDERMODE_VIDEOTERM:
			textScanline = (scanlineIndex - SCANLINE_VIDEOTERM) & 0x7;
			for (UINT32 index = horizIndex; index < horizEnd; index++) {
				UINT32 videoData = shadowMemory[address + index * 2];
				// TO-DO: Move textScanline indexing out of loop
				UINT32 pixelData = 
				characterSetMap[videoData | characterSetMapFlags]
					[textScanline];
				pixels[index] = pixelData;
				videoData = shadowMemory[address + index * 2+ 1];
				// TO-DO: Move textScanline indexing out of loop
				pixelData |= 
				characterSetMap[videoData | characterSetMapFlags]
					[textScanline] << 7;
				pixels[index] = pixelData;
			}
			break;
	}
	
	horizIndex = horizEnd;
}

void BitRenderer::renderScanline(UINT32 scanlineIndex) {
	VideoRenderer::renderScanline(scanlineIndex);
	
	renderScanline(scanlineIndex, APPLEII_PITCH);
	horizIndex = 0;
	
	for (UINT32 index = 0; index < APPLEII_PITCH; index++) {
		for (UINT32 pixelIndex = 0; pixelIndex < 14; pixelIndex++) {
			framebuffer[scanlineIndex * framebufferPitch * 2 + 14 * index + 
						BITRENDERER_XMARGIN + pixelIndex] =
			getBit(pixels[index], (1 << pixelIndex)) ? 0x0fff3f : 0x000000;
			framebuffer[scanlineIndex * framebufferPitch * 2 + 14 * index + 
						BITRENDERER_XMARGIN + pixelIndex + framebufferPitch] =
			getBit(pixels[index], (1 << pixelIndex)) ? 0x0ddf37 : 0x000000;
		}
	}
	
	isFramebufferUpdatedValue = true;
}
